题解 CF1019D 【Large Triangle】

$Description$

给出$N$个点,是否存在$3$个点,它们组成的三角形面积为$S$。

$Solution$

如果确定了三角形的一条边,我们可以将整个坐标系旋转一下,使这条边成为新的$y$轴,这时候我们只要分别对$y$轴的两边分别进行二分即可

我们发现:可以先预处理一下每两个点之间的直线,记录一下起点终点和斜率。

首先所有点按$x$坐标从小到大排序。然后对直线进行极角排序。

当我们处理到直线$AB$时,$AB$就成为了$y$轴,且$A,B$是相邻的(它们距离$y$轴距离都为$0)($假设$A$位置在$B$的上面).然后到下一条直线时,由于斜率从小到大,所以是把整个图顺时针旋转了一点点,直到下一条直线成为$y$轴。那么这个时候把$A,B$在序列中的位置交换一下就行了。因为如果有别的点对相对顺序改变,那么这个点对的斜率一定介于这两条直线之间。下面口胡一下。

假设现在我们以$l_1$为$y$轴。有一组点对,$P$和$Q$,它们相对于$l_1$的横坐标分别是$a$和$b$,其中$a<b$。

然后我们看到比$l_1$斜率更小的第一条直线$l_2$。首先,可以保证,$A$和$B$一定在直线$l_2$的同侧。因为如果$A$和$B$在$l_2$的异侧,那$ABCD$这四个点一定可以生成一条斜率介于$l_1$和$l_2$之间的直线(画一画就知道了),而我们是把斜率排了序,保证了不会有这种情况出现。现在,将整个图再顺时针旋转一点点,使$l_2$成为新的y轴。那么可以保证,$A$和$B$的相对顺序一定是改变了的,而且是把$A$和$B$的排位交换了一下。(可以脑补一下$AB$在$CD$下侧的情况,是同理的).这时候,我们看到$PQ$,在新的y轴下,$P$的横坐标是$c,Q$的横坐标是$d$,假设$c>d$,那么$PQ$的斜率一定比$l_2$的斜率要大,因为$PQ$逆时针旋转一点点就与$l_2$平行。

对于旋转过后排位会变化的点对(除$AB$外),一定会满足$PQ$的条件$:a<b$且$c>d$。但是我们发现它的斜率介于$l_1$和$l_2$之间,然鹅比$l_1$斜率更小的第一条直线是$l_2$,不是$PQ$,与前提矛盾,所以不存在这样的$PQ$。

那么就可以保证从$l_1$旋转到$l_2$时,受影响的就只有$l_1$上的两个点。

$Code$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <bits/stdc++.h>
#define int long long
#define inf 0x3f3f3f3f
#define re register
#define N 40000
#define M 4000500
#define eps 1e-4
using namespace std;
struct point{
int x,y;
friend point operator -(point a,point b){
return (point){a.x-b.x,a.y-b.y};
}
friend int operator ^(point a,point b){
return abs(a.x*b.y-b.x*a.y);
}
}p[N];
struct node{
int u,v;
double d;
}a[M];
inline int read(){
int x=0,w=0;char ch=getchar();
while (!isdigit(ch))w|=ch=='-',ch=getchar();
while (isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return w?-x:x;
}
inline bool cmp(point a,point b){
if (a.x!=b.x)return a.x<b.x;
return a.y<b.y;
}
inline bool cmp1(node a,node b){
return a.d<b.d;
}
inline int area(point a,point b,point c){
return abs(((b-a)^(c-a)));
}
int n,id[N],pos[N],tot,S;
signed main(){
n=read();S=read()*2;
for (int i=1;i<=n;++i)p[i].x=read(),p[i].y=read(),id[i]=pos[i]=i;
sort(p+1,p+1+n,cmp);
for (int i=1;i<=n;++i)
for (int j=1+i;j<=n;++j)
a[++tot]=(node){i,j,atan2((double)(p[j]-p[i]).y,(double)(p[j]-p[i]).x)};
sort(a+1,a+1+tot,cmp1);
for (int i=1;i<=tot;++i){
int x=a[i].u,y=a[i].v;
if (pos[x]>pos[y])swap(x,y);
int l=1,r=pos[x]-1,ans=0;
while (l<=r){
int mid=(l+r)>>1;
if (area(p[id[mid]],p[x],p[y])>=S)ans=mid,l=mid+1;
else r=mid-1;
}
if (area(p[id[ans]],p[x],p[y])==S){
puts("Yes");
printf("%lld %lld\n%lld %lld\n%lld %lld\n",p[id[ans]].x,p[id[ans]].y,p[x].x,p[x].y,p[y].x,p[y].y);
return 0;
}
l=pos[y]+1,r=n,ans=0;
while (l<=r){
int mid=(l+r)>>1;
if (area(p[id[mid]],p[x],p[y])>=S)ans=mid,r=mid-1;
else l=mid+1;
}
if (area(p[id[ans]],p[x],p[y])==S){
puts("Yes");
printf("%lld %lld\n%lld %lld\n%lld %lld\n",p[id[ans]].x,p[id[ans]].y,p[x].x,p[x].y,p[y].x,p[y].y);
return 0;
}
swap(pos[x],pos[y]);
swap(id[pos[x]],id[pos[y]]);
}
puts("No");
return 0;
}